/*
 *  canvas.cpp
 *  krryn
 *
 *  Created by Jasper Bekkers on 9/4/07.
 *  Copyright 2007 Jasper Bekkers. All rights reserved.
 *
 */

#include "canvas.h"
#include "algebra.h"
#include <algorithm>

using namespace krryn::view;
using namespace krryn::math;

int next_power_of_two(int a_Number){
	if(a_Number & (a_Number - 1)){
		return 1 << (static_cast<int>(log(static_cast<float>(a_Number), 2.f)) + 1);
	}else{
		return a_Number;
	}
}

canvas::canvas(int a_Width, int a_Height){
	m_Pitch  = next_power_of_two(a_Width);
	m_Width  = a_Width;
	m_Height = a_Height;

	m_Pixels = new pixel_t[m_Pitch * a_Height];
}

pixel_t *canvas::get_buffer() const{
    return m_Pixels; 
}

int canvas::get_width() const{
    return m_Width;
}

int canvas::get_height() const{
    return m_Height;
}

int canvas::get_pitch() const{
    return m_Pitch;
}

void canvas::paint(int x, int y, pixel_t a_Color){
    m_Pixels[y * get_pitch() + x]  = a_Color;
}

color3 canvas::color_at(int x, int y){
    pixel_t l_Pixel = get_pixel(x, y);
    
    char l_Red, l_Green, l_Blue;

	pixel_t a_Color = m_Pixels[y * get_pitch() + x];

	// WHEN BROKEN, look at the paint() method for a reason

	l_Red   = static_cast<char>((a_Color & 0xFF0000) >> 16);
	l_Green = static_cast<char>((a_Color & 0x00FF00) >> 8);
	l_Blue  = static_cast<char>((a_Color & 0x0000FF));

	return math::color3(
         static_cast<float>(l_Red) / 255.f, 
         static_cast<float>(l_Green) / 255.f, 
         static_cast<float>(l_Blue) / 255.f
    );
}

void canvas::lock(){
    m_Locked = true;
}

void canvas::unlock(){
    m_Locked = false;
}

void canvas::copy_to(canvas *a_Other){
	// TODO: implement
}

void canvas::flip(){
    // SDL_Flip(m_Pixels);
}

pixel_t canvas::mix_color(char r, char g, char b) const{
	//return ((pixel_t)r << 16) + ((pixel_t)g << 8) + (pixel_t)b;
	// THIS MIGHT BE THE REASON color_at is broken, if it's broken
	char l_Temp[4] = {r, g, b, 0};
	return *reinterpret_cast<pixel_t*>(l_Temp);
}

pixel_t canvas::mix_color(const color3 &a_Color) const{
	math::color3 l_Temp = a_Color * 255.f;
	return mix_color(clamp(l_Temp.m_R), clamp(l_Temp.m_G), clamp(l_Temp.m_B));
}

pixel_t canvas::get_pixel(int x, int y){
    if(x < 0) x = abs(x);
    if(y < 0) y = abs(y);

    x %= m_Width;
    y %= m_Height;

	return m_Pixels[x + get_pitch() * y];
}

char canvas::clamp(const float a_Value) const{
    return static_cast<char>(std::max<float>(0, std::min<float>(a_Value, 0xFF)));
}

void canvas::circle(int a_X, int a_Y, int a_Radius, pixel_t a_Color){
    int l_Error = 1 - a_Radius;
    int l_ErrorX = 0;
    int l_ErrorY = -2 * a_Radius;
    int x = 0;
    int y = a_Radius;
    
    pixel_t l_Pixel = a_Color;
    
    while(x < y){
        if(l_Error >= 0){
            y--;
            l_ErrorY += 2;
            l_Error += l_ErrorY;
        }
        x++;
        l_ErrorX += 2;
        l_Error += l_ErrorX + 1;
        
        paint(a_X + x, a_Y + y, l_Pixel);
        paint(a_X - x, a_Y + y, l_Pixel);
        paint(a_X + x, a_Y - y, l_Pixel);
        paint(a_X - x, a_Y - y, l_Pixel);
        
        paint(a_X + y, a_Y + x, l_Pixel);
        paint(a_X - y, a_Y + x, l_Pixel);
        paint(a_X + y, a_Y - x, l_Pixel);
        paint(a_X - y, a_Y - x, l_Pixel);
    }
}
